﻿using In66.Net.Repository.IRepositories.ISqlSugar;

namespace Usr.Api.Applications.Caching
{
    public sealed class CacheService : AbstractCacheService, ICachePreheatable
    {
        private readonly IOptions<JwtConfig> _jwtConfig;

        public CacheService(
            ICacheProvider cacheProvider,
            IServiceProvider serviceProvider,
            IOptions<JwtConfig> jwtConfig)
            : base(cacheProvider, serviceProvider)
        {
            _jwtConfig = jwtConfig;
        }

        public override async Task PreheatAsync()
        {
            //await GetAllOrganizationsFromCacheAsync();
            //await GetAllRelationsFromCacheAsync();
            //await GetAllMenusFromCacheAsync();
            //await GetAllRolesFromCacheAsync();
            //await GetAllMenuCodesFromCacheAsync();
            //await GetOrganizationsSimpleTreeListAsync();
        }

        internal int GetRefreshTokenExpires() =>
            _jwtConfig.Value.RefreshTokenExpire * 60 + _jwtConfig.Value.ClockSkew;

        internal async Task SetFailLoginCountToCacheAsync(long id, int count)
        {
            var cacheKey = ConcatCacheKey(CachingConsts.UserValidatedInfoKeyPrefix, $"FailCount_{id}");
            await CacheProvider.SetAsync(cacheKey, count, TimeSpan.FromSeconds(GetRefreshTokenExpires()));
        }

        internal async Task SetValidateInfoToCacheAsync(UserValidatedInfoDto value)
        {
            var cacheKey = ConcatCacheKey(CachingConsts.UserValidatedInfoKeyPrefix, value.Id);
            await CacheProvider.SetAsync(cacheKey, value, TimeSpan.FromSeconds(GetRefreshTokenExpires()));
        }

        internal async Task<UserValidatedInfoDto> GetUserValidateInfoFromCacheAsync(long id)
        {
            var cacheKey = ConcatCacheKey(CachingConsts.UserValidatedInfoKeyPrefix, id.ToString());
            var cacheValue = await CacheProvider.GetAsync<UserValidatedInfoDto>(cacheKey);
            return cacheValue.Value;
        }

        internal async Task ChangeUserValidateInfoCacheExpiresDtAsync(long id)
        {
            var cacheKey = ConcatCacheKey(CachingConsts.UserValidatedInfoKeyPrefix, id);
            await CacheProvider.KeyExpireAsync(new string[] { cacheKey }, GetRefreshTokenExpires());
        }

        internal async Task<int> GetFailLoginCountByUserIdAsync(long id)
        {
            var cacheKey = ConcatCacheKey(CachingConsts.UserValidatedInfoKeyPrefix, $"FailCount_{id}");
            var cacheValue = await CacheProvider.GetAsync<int>(cacheKey);
            return cacheValue.Value;
        }

        internal async Task<List<OrganizationDto>> GetAllOrganizationsFromCacheAsync()
        {
            var cahceValue = await CacheProvider.GetAsync(CachingConsts.DetpListCacheKey, async () =>
            {
                using var scope = ServiceProvider.CreateScope();
                var orgRepository = scope.ServiceProvider.GetRequiredService<ISugarBaseRepository<Organization>>();
                var allOrganizations = (await orgRepository.QueryListByClauseAsync(p => true)).OrderBy(x => x.Ordinal).ToList();
                return Mapper.Map<List<OrganizationDto>>(allOrganizations);
            }, TimeSpan.FromSeconds(CachingConsts.OneYear));

            return cahceValue.Value;
        }

        internal async Task<List<RelationDto>> GetAllRelationsFromCacheAsync()
        {
            var cahceValue = await CacheProvider.GetAsync(CachingConsts.MenuRelationCacheKey, async () =>
            {
                using var scope = ServiceProvider.CreateScope();
                var relationRepository = scope.ServiceProvider.GetRequiredService<ISugarBaseRepository<RoleRelation>>();
                var allRelations = await relationRepository.QueryListByClauseAsync(p => true);
                return Mapper.Map<List<RelationDto>>(allRelations);
            }, TimeSpan.FromSeconds(CachingConsts.OneYear));

            return cahceValue.Value;
        }

        internal async Task<List<MenuDto>> GetAllMenusFromCacheAsync()
        {
            var cahceValue = await CacheProvider.GetAsync(CachingConsts.MenuListCacheKey, async () =>
            {
                using var scope = ServiceProvider.CreateScope();
                var menuRepository = scope.ServiceProvider.GetRequiredService<ISugarBaseRepository<Menu>>();
                var allMenus = (await menuRepository.QueryListByClauseAsync(p => true)).OrderBy(x => x.Ordinal).ToList();
                return Mapper.Map<List<MenuDto>>(allMenus);
            }, TimeSpan.FromSeconds(CachingConsts.OneYear));

            return cahceValue.Value;
        }

        internal async Task<List<RoleDto>> GetAllRolesFromCacheAsync()
        {
            var cahceValue = await CacheProvider.GetAsync(CachingConsts.RoleListCacheKey, async () =>
            {
                using var scope = ServiceProvider.CreateScope();
                var roleRepository = scope.ServiceProvider.GetRequiredService<ISugarBaseRepository<Role>>();
                var allRoles = (await roleRepository.QueryListByClauseAsync(p => true)).OrderBy(x => x.Ordinal).ToList();
                return Mapper.Map<List<RoleDto>>(allRoles);
            }, TimeSpan.FromSeconds(CachingConsts.OneYear));

            return cahceValue.Value;
        }

        internal async Task<List<RoleMenuCodesDto>> GetAllMenuCodesFromCacheAsync()
        {
            var cahceValue = await CacheProvider.GetAsync(CachingConsts.MenuCodesCacheKey, async () =>
            {
                using var scope = ServiceProvider.CreateScope();
                var relationRepository = scope.ServiceProvider.GetRequiredService<ISugarBaseRepository<RoleRelation>>();
                var allMenus = (await relationRepository.QueryListByClauseAsync(p => true))
                                                                                //.Where(x => x.Menu.Status)
                                                                                .Select(x => new RoleMenuCodesDto { RoleId = x.RoleId/*, Code = x.Menu.Code*/ })
                                                                                .ToList();
                return allMenus.Distinct().ToList();
            }, TimeSpan.FromSeconds(CachingConsts.OneYear));

            return cahceValue.Value;
        }

        internal async Task<List<OrganizationSimpleTreeDto>> GetOrganizationsSimpleTreeListAsync()
        {
            var result = new List<OrganizationSimpleTreeDto>();

            var cacheValue = await CacheProvider.GetAsync<List<OrganizationSimpleTreeDto>>(CachingConsts.DetpSimpleTreeListCacheKey);
            if (cacheValue.HasValue)
            {
                return cacheValue.Value;
            }

            var Organizations = await GetAllOrganizationsFromCacheAsync();

            if (Organizations.IsNullOrEmpty())
            {
                return result;
            }

            var roots = Organizations.Where(d => d.Pid == 0)
                                        .OrderBy(d => d.Ordinal)
                                        .Select(x => new OrganizationSimpleTreeDto { Id = x.Id, Label = x.SimpleName })
                                        .ToList();
            foreach (var node in roots)
            {
                GetChildren(node, Organizations);
                result.Add(node);
            }

            void GetChildren(OrganizationSimpleTreeDto currentNode, List<OrganizationDto> Organizations)
            {
                var childrenNodes = Organizations.Where(d => d.Pid == currentNode.Id)
                                                           .OrderBy(d => d.Ordinal)
                                                           .Select(x => new OrganizationSimpleTreeDto() { Id = x.Id, Label = x.SimpleName })
                                                           .ToList();
                if (childrenNodes.IsNotNullOrEmpty())
                {
                    currentNode.Children.AddRange(childrenNodes);
                    foreach (var node in childrenNodes)
                    {
                        GetChildren(node, Organizations);
                    }
                }
            }

            await CacheProvider.SetAsync(CachingConsts.DetpSimpleTreeListCacheKey, result, TimeSpan.FromSeconds(CachingConsts.OneYear));

            return result;
        }
    }
}
